import java.util.List;
// -------------------------------------------------------------------------
/**
 *  This class represents a block of memory whether taken or free. It keeps
 *  the offset and length of the block and allows for the block to be resized as needed.
 *
 *  @author Erik Rodriguez and Cory Chase
 *  @version Apr 12, 2013
 */
public class MemoryManager{

	private List<MemBlock> freeSpace;	//The List where free space is held
	private int endOfMemory = 0;		//The end of the memory file in bytes

	// ----------------------------------------------------------
	/**
	 * Initialize the free space list
	 */
	public MemoryManager(){
		freeSpace = new MyLinkedList<MemBlock>();
	}

	// ----------------------------------------------------------
	/**
	 * Allocates free space for an incoming sequence based on it's size. Use current
	 * freespace if possible and if necessary will expand the size of the file.
	 * @param sizeInBytes
	 * @return the memory block for the sequence
	 */
	public MemBlock getFreeSpace(int sizeInBytes){
		for(int i= 0; i<freeSpace.size(); i++){ //Expands the file if needed
			if(freeSpace.get(i).getEnd()+1 >= endOfMemory){
				endOfMemory += sizeInBytes - freeSpace.get(i).getLength();
				MemBlock temp = freeSpace.get(i);
				freeSpace.remove(i);

				return temp.expand(sizeInBytes - temp.getLength());
			}
			else if(freeSpace.get(i).getLength() == sizeInBytes){
			    //if existing freespace is an exact match use and remove from list
				MemBlock temp = freeSpace.get(i);
				freeSpace.remove(i);
				return temp;
			}
			else if((freeSpace.get(i).getLength() > sizeInBytes)){
			    //If free space is larger then use necessary amount and shrink free space accordingly
				return freeSpace.get(i).shrink(sizeInBytes);
			}
		}

		MemBlock freeBlock = new MemBlock(sizeInBytes, endOfMemory);
		endOfMemory += sizeInBytes;
		return freeBlock;
	}

	// ----------------------------------------------------------
	/**
	 * When removing a sequence this mehtod will mark the removed space as free space.
	 * It will combine adjacent free spaces if possilble.
	 * @param takenMemory
	 */
	public void freeUp(MemBlock takenMemory){
		MemBlock prevFreeSpace = null;

		takenMemory.convertToByteLength();
		for(int i=0; i<freeSpace.size(); i++){
			if(freeSpace.get(i).getEnd() + 1 == takenMemory.getOffset()){
				freeSpace.get(i).appendMemBlock(takenMemory);
				if((freeSpace.size() > i+1)&&(takenMemory.getEnd() + 1 == freeSpace.get(i+1).getOffset())){
					freeSpace.get(i).appendMemBlock(freeSpace.get(i+1));
					freeSpace.remove(i+1);
					return;
				}
				return;
			}
			else if((freeSpace.size() > i+1)&&(takenMemory.getEnd() + 1 == freeSpace.get(i+1).getOffset())){
				freeSpace.get(i+1).prependMemBlock(takenMemory);
				return;
			}
			else if(prevFreeSpace != null){
				if((prevFreeSpace.getOffset() < takenMemory.getOffset())&&(freeSpace.get(i).getOffset() > takenMemory.getOffset())){
					freeSpace.add(i-1, takenMemory);
					return;
				}
				if(prevFreeSpace.getOffset() > takenMemory.getOffset()){
					break;
				}
			}
			else if(freeSpace.get(i).getOffset() > takenMemory.getOffset()){
				((MyLinkedList<MemBlock>)freeSpace).prepend(takenMemory);
				return;
			}

			prevFreeSpace = freeSpace.get(i);
		}
		freeSpace.add(takenMemory);

	}

	// ----------------------------------------------------------
	/**
	 * Prints the offset and length of all freespaces
	 */
	public void printFreeSpace(){
		if(freeSpace.size() == 0)
			System.out.println("Free Block List: none");
		else
			System.out.println("Free Block List:");
		for(int i=0; i<freeSpace.size(); i++){
			System.out.print("[Block " + (i+1) + "] Starting Byte Location: " + freeSpace.get(i).getOffset() + ", Size ");
			if(freeSpace.get(i).getLength() > 1)
				System.out.print(freeSpace.get(i).getLength() + " bytes\n");
			else
				System.out.print(freeSpace.get(i).getLength() + " byte\n");
		}
	}
	
	/**
	 * Subtracts from the endOfMemory variable
	 * @param adjustBlock
	 */
	public void adjustEndOfMemory(MemBlock adjustBlock){
		endOfMemory -= adjustBlock.getLength();
	}




}
